第 1 章:Docker 進階網路設定
Docker 網路相關命令列表
其中有些命令選項只有在 Docker 服務啟動的時候才能設定,而且不能馬上生效
b BRIDGE or --bridge=BRIDGE
--指定容器掛載的橋接器-bip=CIDR
--定制 docker0 的遮罩H SOCKET... or --host=SOCKET...
--Docker 服務端接收命令的通道-icc=true|false
--是否支援容器之間進行通訊-ip-forward=true|false
--請看下文容器之間的通訊-iptables=true|false
--禁止 Docker 新增 iptables 規則-mtu=BYTES
--容器網路中的 MTU
下面 2 個命令選項既可以在啟動服務時指定,也可以 Docker 容器啟動(docker run
)時候指定
-dns=IP_ADDRESS...
--使用指定的DNS伺服器-dns-search=DOMAIN...
--指定DNS搜尋域
最後這些選項只有在 docker run
執行時使用,因為它是針對容器的特性內容。
h HOSTNAME or --hostname=HOSTNAME
--設定容器主機名-link=CONTAINER_NAME:ALIAS
--新增到另一個容器的連線-net=bridge|none|container:NAME_or_ID|host
--設定容器的橋接模式p SPEC or --publish=SPEC
--映射容器連接埠到宿主主機P or --publish-all=true|false
--映射容器所有連接埠到宿主主機
設定 DNS
Docker 沒有為每個容器專門定制映像檔 → 利用虛擬檔案來掛載到來容器的 3 個相關設定檔案
mount
...
/dev/disk/by-uuid/1fec...ebdf on /etc/hostname type ext4 ...
/dev/disk/by-uuid/1fec...ebdf on /etc/hosts type ext4 ...
tmpfs on /etc/resolv.conf type tmpfs ...
...
讓主機 DNS 訊息發生更新後,所有容器的 dns 設定透過 /etc/resolv.conf
檔案立刻得到更新
如果使用者想要手動指定容器的設定,可以利用下面的選項。
h HOSTNAME or --hostname=HOSTNAME
設定容器的主機名,它會被寫到容器內的/etc/hostname
和/etc/hosts
。但它在容器外部看不到,既不會在docker ps
中顯示,也不會在其他的容器的/etc/hosts
看到。-link=CONTAINER_NAME:ALIAS
選項會在建立容器的時候,新增一個其他容器的主機名到/etc/hosts
檔案中,讓新容器的程式可以使用主機名 ALIAS 就可以連線它。-dns=IP_ADDRESS
新增 DNS 伺服器到容器的/etc/resolv.conf
中,讓容器用這個伺服器來解析所有不在/etc/hosts
中的主機名。-dns-search=DOMAIN
設定容器的搜尋域,當設定搜尋域為.example.com
時,在搜尋一個名為 host 的主機時,DNS 不僅搜尋host,還會搜尋host.example.com
。 注意:如果沒有上述最後 2 個選項,Docker 會預設用主機上的/etc/resolv.conf
來設定容器
容器存取控制
容器的存取控制,主要透過 Linux 上的 iptables
防火墻來進行管理和實作。iptables
是 Linux 上預設的防火墻軟體,在大部分發行版中都內建。
容器存取外部網路
容器要想存取外部網路,需要本地系統的轉發支援。在Linux 系統中,檢查轉發是否打開。
sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1
如果為 0,說明沒有開啟轉發,則需要手動打開。
sysctl -w net.ipv4.ip_forward=1
如果在啟動 Docker 服務的時候設定 --ip-forward=true
, Docker 就會自動設定系統的 ip_forward
參數為 1
容器之間存取
容器之間相互存取,需要兩方面的支援。
容器的網路拓撲是否已經互聯。預設情況下,所有容器都會被連線到 docker0
橋接器上。
本地系統的防火墻軟體 -- iptables
是否允許透過。
存取所有連接埠
當啟動 Docker 服務時候,預設會新增一條轉發策略到 iptables 的 FORWARD 鏈上。策略為透過(ACCEPT
)還是禁止(DROP
)取決於設定--icc=true
(預設值)還是 --icc=false
。當然,如果手動指定 --iptables=false
則不會新增 iptables
規則。
可見,預設情況下,不同容器之間是允許網路互通的。如果為了安全考慮,可以在 /etc/default/docker
檔案中設定 DOCKER_OPTS=--icc=false
來禁止它。
存取指定連接埠
在透過 -icc=false
關閉網路存取後,還可以透過 --link=CONTAINER_NAME:ALIAS
選項來存取容器的開放連接埠。
例如,在啟動 Docker 服務時,可以同時使用 icc=false --iptables=true
參數來關閉允許相互的網路存取,並讓 Docker 可以修改系統中的 iptables
規則。
此時,系統中的 iptables
規則可能是類似
sudo iptables -nL
...
Chain FORWARD (policy ACCEPT)
target prot opt source destination
DROP all -- 0.0.0.0/0 0.0.0.0/0
...
之後,啟動容器(docker run
)時使用 --link=CONTAINER_NAME:ALIAS
選項。Docker 會在 iptable
中為 兩個容器分別新增一條 ACCEPT
規則,允許相互存取開放的連接埠(取決於 Dockerfile 中的 EXPOSE 行)。
當新增了 --link=CONTAINER_NAME:ALIAS
選項後,新增了 iptables
規則。
sudo iptables -nL
...
Chain FORWARD (policy ACCEPT
target prot opt source destination
ACCEPT tcp -- 172.17.0.2 172.17.0.3 tcp spt:80
ACCEPT tcp -- 172.17.0.3 172.17.0.2 tcp dpt:80
DROP all -- 0.0.0.0/0 0.0.0.0/0
...
注意:--link=CONTAINER_NAME:ALIAS
中的 CONTAINER_NAME
目前必須是 Docker 分配的名字,或使用 --name
參數指定的名字。主機名則不會被識別。
埠號映射實作
預設情況下,容器可以主動存取到外部網路的連線,但是外部網路無法存取到容器。
容器存取外部實作
容器所有到外部網路的連線,源位址都會被NAT成本地系統的IP位址。這是使用 iptables
的源位址偽裝操作實作的。
sudo iptables -t nat -nL
...
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 172.17.0.0/16 !172.17.0.0/16
...
其中,上述規則將所有源位址在 172.17.0.0/16
網段,目標位址為其他網段(外部網路)的流量動態偽裝為從系統網卡發出。MASQUERADE 跟傳統 SNAT 的好處是它能動態從網卡取得位址。
外部存取容器實作
容器允許外部存取,可以在 docker run
時候透過 -p
或 -P
參數來啟用。
不管用那種辦法,其實也是在本地的 iptable
的 nat 表中新增相應的規則。
使用 -P
時:
iptables -t nat -nL
...
Chain DOCKER (2 references)
target prot opt source destination
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:49153 to:172.17.0.2:80
使用 -p 80:80
時
iptables -t nat -nL
Chain DOCKER (2 references)
target prot opt source destination
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 to:172.17.0.2:80
這裡的規則映射了 0.0.0.0,意味著將接受主機來自所有介面的流量。使用者可以透過 -p IP:host_port:container_port
或 -p IP::port
來指定允許存取容器的主機上的 IP、介面等,以制定更嚴格的規則。
如果希望永久綁定到某個固定的 IP 位址,可以在 Docker 設定檔案 /etc/default/docker
中指定 DOCKER_OPTS="--ip=IP_ADDRESS"
,之後重啟 Docker 服務即可生效。
設定 docker0 橋接器
Docker 服務預設會建立一個 docker0
橋接器(其上有一個 docker0
內部介面),它在核心層連通了其他的物理或虛擬網卡,這就將所有容器和本地主機都放到同一個物理網路。
Docker 預設指定了 docker0
介面 的 IP 位址和子網遮罩,讓主機和容器之間可以透過橋接器相互通訊,它還給出了 MTU(介面允許接收的最大傳輸單元),通常是 1500 Bytes,或宿主主機網路路由上支援的預設值。這些值都可以在服務啟動的時候進行設定。
-bip=CIDR
-- IP 位址加遮罩格式,例如 192.168.1.5/24-mtu=BYTES
-- 覆蓋預設的 Docker mtu 設定
也可以在設定檔案中設定 DOCKER_OPTS,然後重啟服務。 由於目前 Docker 橋接器是 Linux 橋接器,用者可以使用 brctl show
來查看橋接器和連接埠連線訊息。
sudo brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.3a1d7362b4ee no veth65f9 vethdda6
brctl
命令在 Debian、Ubuntu 中可以使用 sudo apt-get install bridge-utils
來安裝。
每次建立一個新容器的時候,Docker 從可用的位址段中選擇一個未使用的 IP 位址分配給容器的 eth0 連接埠。使用本地主機上 docker0
介面的 IP 作為所有容器的預設網關。
sudo docker run -i -t --rm base /bin/bash
ip addr show eth0
24: eth0: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 32:6f:e0:35:57:91 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.3/16 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::306f:e0ff:fe35:5791/64 scope link
valid_lft forever preferred_lft forever
ip route
default via 172.17.42.1 dev eth0
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.3
exit
自訂橋接器
除了預設的 docker0
橋接器,使用者也可以指定橋接器來連線各個容器。
在啟動 Docker 服務的時候,使用 -b BRIDGE
或--bridge=BRIDGE
來指定使用的橋接器。
如果服務已經執行,那需要先停止服務,並刪除舊的橋接器。
sudo service docker stop
sudo ip link set dev docker0 down
sudo brctl delbr docker0
然後建立一個橋接器 bridge0
。
sudo brctl addbr bridge0
sudo ip addr add 192.168.5.1/24 dev bridge0
sudo ip link set dev bridge0 up
查看確認橋接器建立並啟動。
ip addr show bridge0
4: bridge0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state UP group default
link/ether 66:38:d0:0d:76:18 brd ff:ff:ff:ff:ff:ff
inet 192.168.5.1/24 scope global bridge0
valid_lft forever preferred_lft forever
設定 Docker 服務,預設橋接到建立的橋接器上。
echo 'DOCKER_OPTS="-b=bridge0"' >> /etc/default/docker
sudo service docker start
啟動 Docker 服務。 新建一個容器,可以看到它已經橋接到了 bridge0
上。
可以繼續用 brctl show
命令查看橋接的訊息。另外,在容器中可以使用 ip addr
和 ip route
命令來查看 IP 位址設定和路由訊息。
pipework
Jérôme Petazzoni 編寫了一個叫 pipework 的 shell 腳本,可以幫助使用者在比較復雜的場景中完成容器的連線
playground
Brandon Rhodes 建立了一個提供完整的 Docker 容器網路拓撲管理的 Python庫,包括路由、NAT 防火墻;以及一些提供 HTTP 、 SMTP 、 POP 、 IMAP 、 Telnet 、 SSH 、 FTP 的伺服器。
編輯網路設定檔案
Docker 1.2.0 開始支援在執行中的容器裡編輯 /etc/hosts
、 /etc/hostname
和 /etc/resolve.conf
檔案。
但是這些修改是臨時的,只在執行的容器中保留,容器終止或重啟後並不會被保存下來。也不會被 docker commit
提交。